/**
 * Copyright (C) 2008 aileron.cc
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package cc.aileron.workflow.container.tree;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang.builder.ReflectionToStringBuilder;

import cc.aileron.workflow.WorkflowMethod;

import com.google.inject.Singleton;

/**
 * @author Aileron
 */
@Singleton
public class WorkflowTreeContainerImpl implements WorkflowTreeContainer
{
    /**
     * dynamicFactory
     */
    private static WorkflowTreeElementFactory dynamicFactory = new WorkflowTreeElementFactory()
    {
        @Override
        public WorkflowTreeElementEntry concreate(final String name)
        {
            return new WorkflowTreeElementImpl(name);
        }
    };

    @Override
    public WorkflowTreeContainerResult get(final String uri,
            final WorkflowMethod method, final Set<String> overrideKeys)
    {
        final String extension = getExtension(uri);
        return getDto(uri, method, extension, overrideKeys);
    }

    @Override
    public void put(final WorkflowTree dto)
    {
        final String extension = getExtension(dto.uri);
        final WorkflowTreeElementEntry tree = getWsgiUriTreeEntry(dto.uri);
        tree.setId(dto.method, dto.overrideKey, extension, processId);
        dto.uriParameterKeys = getUriParameterKeys(dto.uri);
        dto.id = processId;
        map.put(processId++, dto);
    }

    @Override
    public String toString()
    {
        return new ReflectionToStringBuilder(this).toString();
    }

    /**
     * WsgiContainerDto
     * 
     * @param uri
     * @return
     */
    private WorkflowTreeContainerResult getDto(final String uri,
            final WorkflowMethod method, final String extension,
            final Set<String> overrideKeys)
    {
        final List<String> uriParameters = new ArrayList<String>();
        final String[] uris = uri.split("/");
        final int max = uris.length;

        int i = 0;
        WorkflowTreeElement current = root;
        while (i < max)
        {
            current = current.get(uris[i++]);
            if (current == null)
            {
                return null;
            }
            if (current.isDynamic())
            {
                final String paramter = uris[i - 1];
                if (i != max)
                {
                    uriParameters.add(paramter);
                }
                else
                {
                    final String value;
                    final int idx = paramter.lastIndexOf('.');
                    if (idx != -1)
                    {
                        value = paramter.substring(0, idx);
                    }
                    else
                    {
                        value = paramter;
                    }
                    uriParameters.add(value);
                }
            }
        }

        final WorkflowTree dto = map.get(getId(current,
                method,
                extension,
                overrideKeys));
        if (dto == null)
        {
            return null;
        }

        final WorkflowTreeContainerResult result = new WorkflowTreeContainerResult();
        result.id = dto.id;
        result.isThrough = dto.isThrough;
        result.uriParameters = new HashMap<String, String>();
        for (int ix = 0, size = dto.uriParameterKeys.size(); ix < size; ix++)
        {
            result.uriParameters.put(dto.uriParameterKeys.get(ix),
                    uriParameters.get(ix));
        }
        return result;
    }

    /**
     * @param uri
     * @return
     */
    private String getExtension(final String uri)
    {
        final String[] tokens = uri.split("\\.");
        if (tokens.length < 2)
        {
            return "";
        }
        return tokens[tokens.length - 1];
    }

    /**
     * @param uriTree
     * @param method
     * @param extension
     * @param overrideKeys
     * @return
     */
    private Integer getId(final WorkflowTreeElement uriTree,
            final WorkflowMethod method, final String extension,
            final Set<String> overrideKeys)
    {
        final Map<String, Integer> oMap = uriTree.getId(method, extension);
        if (oMap == null)
        {
            return 0;
        }
        for (final String key : overrideKeys)
        {
            final Integer id = oMap.get(key);
            if (id != null)
            {
                return id;
            }
        }
        return oMap.get("");
    }

    /**
     * @param uri
     * @return uriParameterKeys
     */
    private List<String> getUriParameterKeys(final String uri)
    {
        final List<String> result = new ArrayList<String>();
        final Matcher matcher = pattern.matcher(uri);
        while (matcher.find())
        {
            final String value = matcher.group(1);
            result.add(value);
        }
        return result;
    }

    /**
     * @param uri
     * @return WsgiUriTreeEntry
     */
    private WorkflowTreeElementEntry getWsgiUriTreeEntry(final String uri)
    {
        final String[] uris = uri.split("/");
        final int max = uris.length;

        int i = 0;
        WorkflowTreeElementEntry base = root;
        WorkflowTreeElementEntry current = base;
        while (i < max)
        {
            final String uriToken = uris[i++];

            base = current;
            current = current.get(uriToken);
            if (current == null)
            {
                current = dynamicFactory.concreate(uriToken);
                base.set(uriToken, current);
            }
        }
        return current;
    }

    /**
     * map
     */
    private final HashMap<Integer, WorkflowTree> map = new HashMap<Integer, WorkflowTree>();

    /**
     * pattern
     */
    private final Pattern pattern = Pattern.compile(Pattern.quote("${")
            + "(.*?)" + Pattern.quote("}"));

    /**
     * process-id
     */
    private int processId = 1;

    /**
     * root
     */
    private final WorkflowTreeElementEntry root = new WorkflowTreeElementImpl("/");
}